package com.webserver.core;

import com.webserver.annotation.Controller;
import com.webserver.annotation.RequestMapping;

import java.io.File;
import java.io.FileFilter;
import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

/**
 * 作者：JustOne
 * 微信：Andylau2020smile
 * 抖音：JustOne2018
 * 日期：2022/6/26 23:08
 */

/*
维护所有[请求] 与 [处理该请求的Controller中业务方法] 的对应关系
使得DispatcherServlet可以方便的获取请求对应的处理方法来进行反射调用
也即是：
key是 String
value 是 Controller类 +  对应的处理方法
所以我们需要把 [ Controller类 +  对应的处理方法 ] 当成一个整体，可以考虑写成一个类的，它两就是类的两个属性，
且由于它只是服务于本类，所以可以写成本类的内部类
于是，创建一个内部类，收MethodMapping.
*/


public class HandlerMapping {
    //存放 [ Controller类 +  对应的处理方法 ]
    private static Map<String, MethodMapping> map = new HashMap<>();

    static {
        initMapping();
    }

    ////////
    private static void initMapping(){
        //1,扫描com/webserver/controller目录，根据该目录下所有的Xxxx.class文件的文件名来获取所有的类名。
        //2,通过"com.webserver.controller"+类名，即可获取该包下所有的类对象。
        //3,有了类对象就可以判断该类是否有被@Controller标记，只有标记过的才是处理业务的类。
        //4,谁被标记过，就获取它里面被@RequestMapping标记过的方法，以及该注解的值。
        //5,谁的@RequestMapping注解值与 用户的请求 youUri相同，就调用谁来处理业务。
        try {
            //原来：URL url = DispatcherServlet.class.getClassLoader().getResource("./com/webserver/controller");
            //现在：我们要求程序员必须把他的controller包放在与启动类WebServerApplication.java所在的目录下
            URL url = WebServerApplication.wsaClazz.getResource("./controller");
            File dir = new File(url.toURI());
            FileFilter filter = (f) -> f.isFile() && f.getName().endsWith(".class");
            File[] fileArr = dir.listFiles(filter);
            for(File file : fileArr){
                String fileName = file.getName();//-------------------Bird.class
                String className =fileName.split("\\.")[0];//---Bird
                System.out.println("类全名是：" + "com.webserver.controller." + className);
                Class clazz = Class.forName("com.webserver.controller." + className);

                //挑出类上有@Controller注解的类，然后获取该类中 每个方法的注解值
                if( clazz.isAnnotationPresent(Controller.class) ){
                    Object controller = clazz.newInstance();//===============>我们要找的controller类
                    Method[] methods = clazz.getDeclaredMethods();
                    for(Method method : methods ){
                        boolean haveRm = method.isAnnotationPresent(RequestMapping.class);//是否有该注解
                        String annoValue = haveRm  ?  method.getAnnotation(RequestMapping.class).value() : "";
                        //如果有注解值，就是我们要找的方法
                        if( !annoValue.isEmpty() ){
                            MethodMapping methodMapping = new MethodMapping(controller, method); //类 和 方法
                            map.put(annoValue, methodMapping);//  k=请求, v=类+方法
                        }
                    }
                }
            }
        } catch (URISyntaxException | ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据给定的请求路径获取处理该请求的Controller以及对应的处理方法
     */
    public static MethodMapping getMethod(String path){
        return map.get(path);
    }


    //--------------------------
    //内部类
    //用于封装[ Controller类 +  对应的处理方法 ]
    //代码基本上全是自动生成。
    //定义成static，是因为外围类是在静态块中调用，没有必要创建对象。
    public static class MethodMapping{
        private Object controller;
        private Method method;

        public MethodMapping() {

        }

        public MethodMapping(Object controller, Method method) {
            this.controller = controller;
            this.method = method;
        }

        public Object getController() {
            return controller;
        }

        public void setController(Object controller) {
            this.controller = controller;
        }

        public Method getMethod() {
            return method;
        }

        public void setMethod(Method method) {
            this.method = method;
        }
    }

}















